package com.epoch.base.export;

import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;

import com.epoch.base.util.JSONUtils;
import com.epoch.base.util.LoggerUtil;
import com.jfinal.aop.Interceptor;
import com.jfinal.aop.Invocation;
import com.jfinal.core.Controller;
import com.jfinal.core.JFinal;
import com.jfinal.kit.PathKit;
import com.jfinal.plugin.activerecord.Page;

public class ExportInterceptor implements Interceptor {
    private static final Logger LOGGER = LoggerUtil.getLogger();
    public static final Map<String, IFileExporter> EXPORTER_MAP = new HashMap<String, IFileExporter>();
    static {
        ExportInterceptor.EXPORTER_MAP.put(ExportConstant.FILE_TYPE_JSON, new JsonExporter());
        ExportInterceptor.EXPORTER_MAP.put(ExportConstant.FILE_TYPE_XML, new XmlExporter());
        ExportInterceptor.EXPORTER_MAP.put(ExportConstant.FILE_TYPE_CSV, new CsvExporter(",", true));
        ExportInterceptor.EXPORTER_MAP.put(ExportConstant.FILE_TYPE_TXT, new TxtExporter());
        ExportInterceptor.EXPORTER_MAP.put(ExportConstant.FILE_TYPE_SQL, new SqlExporter());
        ExportInterceptor.EXPORTER_MAP.put(ExportConstant.FILE_TYPE_DOC, new DocExporter());
        ExportInterceptor.EXPORTER_MAP.put(ExportConstant.FILE_TYPE_EXCEL, new XlsExporter());
        ExportInterceptor.EXPORTER_MAP.put(ExportConstant.FILE_TYPE_XLSX, new XlsxExporter());
    }

    public static void addExporter(String fileType, IFileExporter exporter) {
        ExportInterceptor.EXPORTER_MAP.put(fileType, exporter);
    }

    @Override
    public void intercept(Invocation inv) {
        Controller c = inv.getController();
        boolean serverExport = Boolean.TRUE.equals(c.getParaToBoolean(ExportConstant.SERVER_EXPORT));
        inv.invoke();
        if (serverExport) {
            // 后台导出查询后处理
            Object expObj = c.getAttr(ExportConstant.EXPORT_DATA);
            Collection<?> data = null;
            if (expObj instanceof Page) {
                Page<?> expData = (Page<?>)expObj;
                data = expData.getList();
            } else if (expObj instanceof Collection) {
                data = (Collection<?>)expObj;
            }
            this.exportData(c, data);
        }
    }

    private void exportData(Controller c, Collection<?> data) {
        String fileType = c.getPara(ExportConstant.EXPORT_FILE_TYPE);
        IFileExporter exporter = ExportInterceptor.EXPORTER_MAP.get(fileType);
        if (exporter == null) {
            c.renderText("Unsupported server export file type '" + fileType + "'.");
            return;
        }
        String headMetaJson = c.getPara(ExportConstant.EXPORT_HEAD_META);
        List<HeadMeta> headMetas = JSONUtils.toList(headMetaJson, HeadMeta.class);
        Map<Integer, IExportConvertor> indexCvts = new HashMap<Integer, IExportConvertor>();
        Map<String, IExportConvertor> fieldCvts = new HashMap<String, IExportConvertor>();
        if (headMetas != null) {
            int cols = headMetas.size();
            for (int col = 0; col < cols; col++) {
                HeadMeta meta = headMetas.get(col);
                String exportCvt = meta.getExportCvt();
                if (StringUtils.isNotEmpty(exportCvt) && !StringUtils.equalsIgnoreCase(exportCvt, "null")) {
                    IExportConvertor cvt = null;
                    try {
                        Class<?> cvtCls = Class.forName(exportCvt);
                        if (IExportConvertor.class.isAssignableFrom(cvtCls)) {
                            Object cvtObj = cvtCls.newInstance();
                            cvt = (IExportConvertor)cvtObj;
                            String cvtInitParam = meta.getCvtInitParam();
                            cvt.init(cvtInitParam);
                        } else {
                            LOGGER.error("specific export convertor '{}' does not implement the interface '{}'.",
                                exportCvt, IExportConvertor.class.getName());
                        }
                    } catch (ClassNotFoundException e) {
                        LOGGER.error("invalid export convertor '" + exportCvt + "'", e);
                    } catch (ReflectiveOperationException e) {
                        LOGGER.error("can not instantiate export convertor '" + exportCvt + "'", e);
                    }
                    if (cvt != null) {
                        indexCvts.put(Integer.valueOf(col), cvt);
                    }
                }
            }
        }
        Map<?, IExportConvertor>[] customCvts = c.getAttr(ExportConstant.EXPORT_CVTS);
        if (customCvts != null) {
            if (customCvts.length > 0 && customCvts[0] != null) {
                @SuppressWarnings("unchecked")
                Map<Integer, IExportConvertor> customIndexCvts = (Map<Integer, IExportConvertor>)customCvts[1];
                indexCvts.putAll(customIndexCvts);
            }
            if (customCvts.length > 1 && customCvts[1] != null) {
                @SuppressWarnings("unchecked")
                Map<String, IExportConvertor> customFieldCvts = (Map<String, IExportConvertor>)customCvts[0];
                fieldCvts.putAll(customFieldCvts);
            }
        }

        Map<String, Object> params = null;
        if (ExportConstant.FILE_TYPE_EXCEL.equals(fileType) || ExportConstant.FILE_TYPE_XLSX.equals(fileType)) {
            params = new HashMap<String, Object>(1);
            params.put(ExportConstant.EXPORT_SHEET_NAME, c.getPara(ExportConstant.EXPORT_SHEET_NAME));
        } else if (ExportConstant.FILE_TYPE_SQL.equals(fileType)) {
            params = new HashMap<String, Object>(1);
            params.put(ExportConstant.EXPORT_TABLE_NAME, c.getPara(ExportConstant.EXPORT_TABLE_NAME));
        }
        String folderPath = PathKit.getWebRootPath() + File.separator
            + JFinal.me().getConstants().getBaseDownloadPath();
        try {
            File file = exporter.export(data, folderPath, headMetas, indexCvts, fieldCvts, params);
            String fileName = c.getPara(ExportConstant.EXPORT_FILE_NAME);
            if (StringUtils.isEmpty(fileName)) {
                fileName = ExportConstant.DEF_FILE_NAME;
            }
            fileName += exporter.getFileNameExt();
            c.renderFile(file, fileName);
        } catch (ExportException e) {
            LOGGER.error("export failed", e);
            c.renderText(e.getI18nErrMsg());
        }
    }
}
